using UnityEngine;
using System.Collections;
using DanielSig.UnityExtensions;
using System;


/// <summary>
/// 32 Bit Unit Vector. Very efficient way of storing a unit vector with a precision error of
/// 0.00001 square degrees. This data structure has very low noise ratio (less than 0.0001%),
/// as opposed to unit vectors in euclidean space using 3 single precision (3 * 32bits = 96bits)
/// floating point values (more than 99.99% noise). This data structure also has precisions much
/// more evenly distributed, as opposed to using eular angles where the poles have most precision.
/// This data structure is mainly designed for networking, where data efficiency is important,
/// but not the speed of computability. Use this structure for sending and storing unit vectors.
/// </summary>
namespace DanielSig.Geometry
{
	public struct UnitVector32 : I32Bit
	{
		
		public static implicit operator Vector3(UnitVector32 unitVector)
		{
			return unitVector.vector;
		}
		public static explicit operator UnitVector32(Vector3 vector)
		{
			return new UnitVector32(vector);
		}
		
		public UnitVector32(Vector3 vector)
		{
			_data = 0;
			this.vector = vector;
		}
		public UnitVector32(uint data)
		{
			_data = data;
		}
	
		public Vector3 vector
		{
			get
			{
				float u = uValue;
				float v = vValue;
				bool back = (u + v) < 1F;
				if(!back)
				{
					float temp = u;
					u = 1F - v;
					v = 1F - temp;
				}
				Vector3 start = back ? Vector3.back : Vector3.forward;
				Vector3 uDir;
				Vector3 vDir;
				if((_data & UP_BIT) > 0)
				{
					if((_data & RIGHT_BIT) > 0)
					{
						uDir = back ? Vector3.up - Vector3.back : Vector3.right - Vector3.forward;
						vDir = back ? Vector3.right - Vector3.back : Vector3.up - Vector3.forward;
					}
					else
					{
						uDir = back ? Vector3.up - Vector3.back : Vector3.left - Vector3.forward;
						vDir = back ? Vector3.left - Vector3.back : Vector3.up - Vector3.forward;
					}
				}
				else if((_data & RIGHT_BIT) > 0)
				{
					uDir = back ? Vector3.down - Vector3.back : Vector3.right - Vector3.forward;
					vDir = back ? Vector3.right - Vector3.back : Vector3.down - Vector3.forward;
				}
				else
				{
					uDir = back ? Vector3.down - Vector3.back : Vector3.left - Vector3.forward;
					vDir = back ? Vector3.left - Vector3.back : Vector3.down - Vector3.forward;
				}
				return (start + uDir * u + vDir * v).normalized;
			}
			set
			{
				value.Normalize();
				bool back = value.z < 0F;
				Vector3 start = back ? Vector3.back : Vector3.forward;
				Vector3 uDir;
				Vector3 vDir;
				_data = 0;
				if(value.y > 0F)
				{
					_data |= UP_BIT;
					if(value.x > 0F)
					{
						_data |= RIGHT_BIT;
						uDir = back ? Vector3.up - Vector3.back : Vector3.up - Vector3.forward;
						vDir = back ? Vector3.right - Vector3.back : Vector3.right - Vector3.forward;
					}
					else
					{
						uDir = back ? Vector3.up - Vector3.back : Vector3.up - Vector3.forward;
						vDir = back ? Vector3.left - Vector3.back : Vector3.left - Vector3.forward;
					}
				}
				else if(value.x > 0F)
				{
					_data |= RIGHT_BIT;
					uDir = back ? Vector3.down - Vector3.back : Vector3.down - Vector3.forward;
					vDir = back ? Vector3.right - Vector3.back : Vector3.right - Vector3.forward;
				}
				else
				{
					uDir = back ? Vector3.down - Vector3.back : Vector3.down - Vector3.forward;
					vDir = back ? Vector3.left - Vector3.back : Vector3.left - Vector3.forward;
				}
				float uLen = 1F / uDir.magnitude;
				float vLen = 1F / vDir.magnitude;
				Vector3 uNormal = uDir * uLen;
				Vector3 vNormal = vDir * vLen;
				Plane p = new Plane(uNormal.Cross(vNormal).normalized, start);
				Vector3 flat = p.CutLine(Vector3.zero, value*5F);
				
				float v = Mathf.Sqrt(flat.x * flat.x * 2) * vLen;
				Vector3 r = flat - vDir * v;
				float u = (r - start).magnitude * vLen;
				
				if(!back)
				{
					u = 1F - u;
					v = 1F - v;
				}
				
				_data |= (uint)(((uint)(u * UV_MAX + 0.5F) & UV_MAX) << 15);
				_data |= (uint)((uint)(v * UV_MAX + 0.5F) & UV_MAX);
			}
		}
		
		private float uValue
		{
			get
			{
				return ((_data >> 15) & UV_MAX) * INV_UV_MAX;
			}
			set
			{
				_data = (uint)(_data & (~U_VALUE));
				_data |= (uint)(((uint)(value * UV_MAX + 0.5F) << 15) & U_VALUE);
			}
		}
		private float vValue
		{
			get
			{
				return (_data & UV_MAX) * INV_UV_MAX;
			}
			set
			{
				_data = (uint)(_data & (~V_VALUE));
				_data |= (uint)((uint)(value * UV_MAX + 0.5F) & V_VALUE);
			}
		}
	
		public uint value
		{
			get
			{
				return _data;
			}
			set
			{
				_data = value;
			}
		}
		public uint rawData
		{
			get
			{
				return _data;
			}
			set
			{
				_data = value;
			}
		}
		public override int GetHashCode()
		{
			return BitConverter.ToInt32(BitConverter.GetBytes(_data), 0);
		}
		public override bool Equals (object obj)
		{
			if(obj is UnitVector32)
				return ((UnitVector32)obj)._data == _data;
			return base.Equals (obj);
		}
		public static bool operator ==(UnitVector32 left, UnitVector32 right)
		{
			return left._data == right._data;
		}
		public static bool operator !=(UnitVector32 left, UnitVector32 right)
		{
			return left._data != right._data;
		}
		
		private uint _data;
		
		/*
		1000 0000 0000 0000 0000 0000 0000 0000
		0100 0000 0000 0000 0000 0000 0000 0000
		0011 1111 1111 1111 1000 0000 0000 0000
		0000 0000 0000 0000 0111 1111 1111 1111
		*/
		private const uint UP_BIT = 	0x80000000;
		private const uint RIGHT_BIT = 	0x40000000;
		private const uint U_VALUE = 	0x3FFF8000;
		private const uint V_VALUE = 	0x00007FFF;
		private const uint UV_MAX = V_VALUE;
		private const float INV_UV_MAX = 1F / UV_MAX;
	}
}